package com.gitee.feizns.web.utils;

import com.alibaba.fastjson.JSONObject;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * @link https://blog.csdn.net/wangmx1993328/article/details/83856391
 * @author feizns
 * @since 2019/5/7
 */
public class QrCodeUtils {

    /**
     * CODE_WIDTH：二维码宽度，单位像素
     * CODE_HEIGHT：二维码高度，单位像素
     * FRONT_COLOR：二维码前景色，0x000000 表示黑色
     * BACKGROUND_COLOR：二维码背景色，0xFFFFFF 表示白色
     * 演示用 16 进制表示，和前端页面 CSS 的取色是一样的，注意前后景颜色应该对比明显，如常见的黑白
     */
    private static final int CODE_WIDTH = 400;

    private static final int CODE_HEIGHT = 400;

    private static final int FRONT_COLOR = 0x000000;

    private static final int BACKGROUND_COLOR = 0xFFFFFF;

    /**
     * 默认的字符编码
     */
    public static final String DEFAULT_CHARSET_UTF8 = "UTF-8";

    /**
     * 默认的格式化名称
     */
    public static final String DEFAULT_FORMAT_NAME = "png";

    /**
     * 默认的外边距
     */
    public static final int DEFAULT_MARGIN_1 = 1;

    /**
     * 默认配置
     */
    private static final Map<EncodeHintType, Object> DEFAULT_HINTS_CONFIG = new HashMap<>();

    static {
        DEFAULT_HINTS_CONFIG.put(EncodeHintType.CHARACTER_SET, DEFAULT_CHARSET_UTF8);
        DEFAULT_HINTS_CONFIG.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        DEFAULT_HINTS_CONFIG.put(EncodeHintType.MARGIN, DEFAULT_MARGIN_1);
    }

    public static BufferedImage generatorQRCode(String content) throws WriterException {
        return generatorQRCode(content, CODE_WIDTH, CODE_HEIGHT);
    }

    public static InputStream generatorQRCodeIs(String content, String formatName) throws WriterException, IOException {
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
            BufferedImage image = generatorQRCode(content);
            ImageIO.write(image, formatName, bos);
            return new ByteArrayInputStream(bos.toByteArray());
        }
    }

    public static InputStream generatorQRCodeIs(Object obj) throws IOException, WriterException {
        return generatorQRCodeIs(JSONObject.toJSONString(obj), DEFAULT_FORMAT_NAME);
    }

    /**
     * 生成二维码图片
     * @param content
     * @param width
     * @param height
     * @return
     * @throws WriterException
     */
    public static BufferedImage generatorQRCode(String content, int width, int height) throws WriterException {
        /**
         * MultiFormatWriter:多格式写入，这是一个工厂类，里面重载了两个 encode 方法，用于写入条形码或二维码
         *      encode(String contents,BarcodeFormat format,int width, int height,Map<EncodeHintType,?> hints)
         *      contents:条形码/二维码内容
         *      format：编码类型，如 条形码，二维码 等
         *      width：码的宽度
         *      height：码的高度
         *      hints：码内容的编码类型
         * BarcodeFormat：枚举该程序包已知的条形码格式，即创建何种码，如 1 维的条形码，2 维的二维码 等
         * BitMatrix：位(比特)矩阵或叫2D矩阵，也就是需要的二维码
         */
        MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
        BitMatrix bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, width, height, DEFAULT_HINTS_CONFIG);

        /**java.awt.image.BufferedImage：具有图像数据的可访问缓冲图像，实现了 RenderedImage 接口
         * BitMatrix 的 get(int x, int y) 获取比特矩阵内容，指定位置有值，则返回true，将其设置为前景色，否则设置为背景色
         * BufferedImage 的 setRGB(int x, int y, int rgb) 方法设置图像像素
         *      x：像素位置的横坐标，即列
         *      y：像素位置的纵坐标，即行
         *      rgb：像素的值，采用 16 进制,如 0xFFFFFF 白色
         */
        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                bufferedImage.setRGB(x, y, bitMatrix.get(x, y) ? FRONT_COLOR : BACKGROUND_COLOR);
            }
        }

        return bufferedImage;
    }

}
